package com.example.springbootflowabledemo.service.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.springbootflowabledemo.domian.R;
import com.example.springbootflowabledemo.domian.dto.FlowTaskDto;
import com.example.springbootflowabledemo.domian.dto.TodoListDto;
import com.example.springbootflowabledemo.domian.pojo.sys.SysUser;
import com.example.springbootflowabledemo.domian.resp.MyProcessResp;
import com.example.springbootflowabledemo.domian.resp.PageResp;
import com.example.springbootflowabledemo.service.FlowableService;
import com.example.springbootflowabledemo.service.sys.SysUserService;
import com.example.springbootflowabledemo.util.ContextHolderUtil;
import liquibase.repackaged.org.apache.commons.collections4.CollectionUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.EndEvent;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.Process;
import org.flowable.common.engine.impl.identity.Authentication;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.impl.persistence.entity.HistoricProcessInstanceEntityImpl;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.flowable.ui.modeler.domain.AbstractModel;
import org.flowable.ui.modeler.serviceapi.ModelService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
@Transactional(rollbackFor = Exception.class)
public class FlowableServiceImpl implements FlowableService {

    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private TaskService taskService;

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private HistoryService historyService;

    @Autowired
    private ModelService modelService;

    private static final String BPMN_FILE_SUFFIX = ".bpmn";

    @Autowired
    private SysUserService sysUserService;


    @Override
    public void deployment(String name, InputStream in) {
        Deployment deploy = repositoryService.createDeployment().addInputStream(name + BPMN_FILE_SUFFIX, in).name(name).deploy();
        log.info("流程部署id={}", deploy.getId());
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deploy.getId()).singleResult();
        log.info("(启动流程使用)流程processDefinitionId={}", processDefinition.getId());
    }


    @Override
    public void activate(String procdefId, String title, Map<String, Object> variables) {
        boolean suspended = repositoryService.getProcessDefinition(procdefId).isSuspended();
        if (suspended) {
            throw new RuntimeException("流程定义被挂起, 无法启动.");
        }
        Authentication.setAuthenticatedUserId(ContextHolderUtil.getUserId().toString());
        ProcessInstance processInstance = runtimeService.createProcessInstanceBuilder()
                .name(title)  //流程标题
                .processDefinitionId(procdefId)   //流程模板id
                .variables(variables)        //流程参数
                .start();

        log.info("流程id={}", processInstance.getId());

        List<Task> tasks = taskService.createTaskQuery().processInstanceId(processInstance.getId()).list();
        for (Task task : tasks) {
            log.info("流程id={}, 下次执行task={}", processInstance.getId(), task.getId());
        }
    }

    @Override
    public void complete(String taskId, Map<String, Object> variables) {
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (task.isSuspended()) {
            log.error("流程实例被挂起, 无法处理task, taskId={}, processId={}", taskId, task.getProcessInstanceId());
            throw new RuntimeException("流程实例被挂起, 无法处理task.");
        }
//        taskService.setAssignee(taskId, ContextHolderUtil.getUserId().toString());
        log.info("开始处理节点>>>>>>>>>>>>");
        taskService.complete(taskId, variables);
        log.info("结束处理节点>>>>>>>>>>>>");
    }

    @Override
    public void deleteDeploy(String deployId) {
        log.info("删除流程定义成功deployId={}", deployId);
        // true 级联删除, 删除该流程定义相关的所有数据
        repositoryService.deleteDeployment(deployId, true);
    }

    @Override
    public String updateDeployState(String procdefId, Integer state) {
        if (state == null) {
            log.error("deployId={}, 激活/挂起流程定义状态错误.", procdefId);
            return "激活/挂起流程定义状态错误.";
        }
        if (1 == state) {
            repositoryService.activateProcessDefinitionById(procdefId, true, new Date());
            return "流程定义激活成功.";
        }

        if (2 == state) {
            repositoryService.suspendProcessDefinitionById(procdefId, true, new Date());
            return "流程定义挂机成功.";
        }
        return "激活/挂起流程定义失败.";
    }

    @Override
    public String updateProcessState(String processInstanceId, Integer state) {
        if (state == null) {
            log.error("processInstanceId={}, 激活/挂起流程实例失败, 状态错误", processInstanceId);
            return "激活/挂起流程实例状态错误.";
        }

        if (1 == state) {
            runtimeService.activateProcessInstanceById(processInstanceId);
            return "流程实例激活成功.";
        }

        if (2 == state) {
            runtimeService.suspendProcessInstanceById(processInstanceId);
            return "流程实例挂机成功.";
        }
        return "激活/挂起流程实例失败.";
    }

    @Override
    public List<MyProcessResp> myStartProcess() {
        List<HistoricProcessInstance> historicProcessInstances = historyService.createHistoricProcessInstanceQuery().startedBy(ContextHolderUtil.getUserId().toString()).orderByProcessInstanceStartTime().desc().list();

        List<MyProcessResp> resps = new ArrayList<>();
        for (HistoricProcessInstance historicProcessInstance : historicProcessInstances) {
            MyProcessResp resp = new MyProcessResp();
            resp.setProcessDefinitionId(historicProcessInstance.getProcessDefinitionId());
            resp.setStartDate(historicProcessInstance.getStartTime());
            HistoricProcessInstanceEntityImpl historicProcessInstanceEntity = (HistoricProcessInstanceEntityImpl) historicProcessInstance;
            resp.setProcessInstanceId(historicProcessInstanceEntity.getProcessInstanceId());
            resp.setEndDate(historicProcessInstanceEntity.getEndTime());
            resp.setProcessDefinitionName(historicProcessInstance.getProcessDefinitionName());
            resp.setTitle(historicProcessInstance.getName());
            resps.add(resp);
        }
        return resps;
    }

    @Override
    public R myTodoList(Integer pageNum, Integer pageSize) {
        Page<FlowTaskDto> page = new Page<>();
        Long userId = ContextHolderUtil.getUserId();
        TaskQuery taskQuery = taskService.createTaskQuery()
                .active()
                .includeProcessVariables()
                .taskAssignee(userId.toString())
                .orderByTaskCreateTime().desc();
        page.setTotal(taskQuery.count());
        List<Task> taskList = taskQuery.listPage(pageSize * (pageNum - 1), pageSize);
        List<FlowTaskDto> flowList = new ArrayList<>();
        for (Task task : taskList) {
            FlowTaskDto flowTask = new FlowTaskDto();
            // 当前流程信息
            flowTask.setTaskId(task.getId());
            flowTask.setTaskDefKey(task.getTaskDefinitionKey());
            flowTask.setCreateTime(task.getCreateTime());
            flowTask.setProcDefId(task.getProcessDefinitionId());
            flowTask.setExecutionId(task.getExecutionId());
            flowTask.setTaskName(task.getName());
            // 流程定义信息
            ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()
                    .processDefinitionId(task.getProcessDefinitionId())
                    .singleResult();
            flowTask.setDeployId(pd.getDeploymentId());
            flowTask.setProcDefName(pd.getName());
            flowTask.setProcDefVersion(pd.getVersion());
            flowTask.setProcInsId(task.getProcessInstanceId());

            // 流程发起人信息
            HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
                    .processInstanceId(task.getProcessInstanceId())
                    .singleResult();
            SysUser startUser = sysUserService.getUserById(historicProcessInstance.getStartUserId());
//            SysUser startUser = sysUserService.selectUserById(Long.parseLong(task.getAssignee()));
            flowTask.setStartUserId(startUser.getId());
            flowTask.setStartUserName(startUser.getNickname());
//            flowTask.setStartDeptName(startUser.getDept().getDeptName());
            flowList.add(flowTask);
        }

        page.setRecords(flowList);
        return R.success(page);
    }

    @Override
    public void importTemplate(String processModelId, String processModelHistoryId) {
        log.info("从flowable-ui直接导入流程模板processModelId={}, processModelHistoryId={}", processModelId, processModelHistoryId);
        AbstractModel model;
        //获取UI流程模板
        if (StringUtils.isBlank(processModelHistoryId)) {
            model = modelService.getModel(processModelId);
        } else {
            model = modelService.getModelHistory(processModelId, processModelHistoryId);
        }
        deployTemplate(model);
    }

    @Override
    public void stopProcessInstance(String processInstanceId) {
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        if (processInstance != null) {
            //一、获取终止节点
            List<EndEvent> endNodes = findEndFlowElement(processInstance.getProcessDefinitionId());
            String endId = endNodes.get(0).getId();
            //二、执行终止
            List<Execution> executions = runtimeService.createExecutionQuery().parentId(processInstanceId).list();
            List<String> executionIds = new ArrayList<>();
            executions.forEach(execution -> executionIds.add(execution.getId()));
            runtimeService.createChangeActivityStateBuilder().moveExecutionsToSingleActivityId(executionIds, endId).changeState();
            log.info("终止processInstanceId:{}成功", processInstanceId);
        } else {
            log.info("不存在运行的实例processInstanceId:{}", processInstanceId);
        }
    }

    private List findEndFlowElement(String processDefId) {
        Process mainProcess = repositoryService.getBpmnModel(processDefId).getMainProcess();
        Collection<FlowElement> list = mainProcess.getFlowElements();
        if (CollectionUtils.isEmpty(list)) {
            return Collections.EMPTY_LIST;
        }
        return list.stream().filter(f -> f instanceof EndEvent).collect(Collectors.toList());
    }

    /**
     * 部署模板
     *
     * @param model
     * @return
     */
    private void deployTemplate(AbstractModel model) {
        //获取导入文件的流
        BpmnModel bpmnModel = modelService.getBpmnModel(model);
        byte[] xmlBytes = modelService.getBpmnXML(bpmnModel);
        BufferedInputStream in = new BufferedInputStream(new ByteArrayInputStream(xmlBytes));
        //流程部署导入
        importFile(in, model.getName());
    }

    private void importFile(InputStream in, String name) {
        Deployment deploy = repositoryService.createDeployment().addInputStream(name + BPMN_FILE_SUFFIX, in).name(name).deploy();
        log.info("导入流程模板成功, name={}, deployId={}", name, deploy.getId());
    }
}
